import subprocess
from abc import ABC, abstractmethod
from typing import Generator, Optional

class TranscodingStage(ABC):
    @abstractmethod
    def process(self, input_stream) -> Generator[bytes, None, None]:
        pass

class FFmpegTranscoder:
    def __init__(self, preset: dict):
        self.preset = preset
        self.process = None
        
    def start(self, input_path: str) -> None:
        cmd = [
            'ffmpeg',
            '-i', input_path,
            '-c:a', self.preset['codec'],
            '-ar', str(self.preset['sample_rate']),
            '-b:a', str(self.preset['bitrate']),
            '-f', self.preset['format'],
            'pipe:1'
        ]
        self.process = subprocess.Popen(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE
        )
        
    def read_chunk(self, size: int = 4096) -> Optional[bytes]:
        if self.process.poll() is not None:
            return None
        return self.process.stdout.read(size)
        
    def cancel(self) -> None:
        if self.process:
            self.process.terminate()
            self.process.wait()

class RealTimePipeline:
    def __init__(self, stages: List[TranscodingStage]):
        self.stages = stages
        
    def run(self, input_source) -> Generator[bytes, None, None]:
        current_stream = input_source
        for stage in self.stages:
            current_stream = stage.process(current_stream)
            
        for chunk in current_stream:
            yield chunk